/**
 * Copyright 2010, Lars J. Nilsson <http://www.larsan.net> 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.larsan.dconf.jbc;

import static net.larsan.dconf.jbc.JBCNode.toJbcNode;
import static net.larsan.dconf.jbc.JBCNode.toJdbcNodeList;
import static net.larsan.dconf.jbc.PathNormalizer.DOMAINS;
import static net.larsan.dconf.jbc.PathNormalizer.ROOT;
import static net.larsan.dconf.jbc.PathNormalizer.ROOT_DOMAIN;
import static org.apache.commons.lang.Validate.notNull;

import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import net.larsan.dconf.DNode;
import net.larsan.dconf.DNodeStore;
import net.larsan.dconf.DPath;
import net.larsan.dconf.NoSuchDNodeException;
import net.larsan.dconf.util.DPaths;
import net.larsan.dconf.util.ExtendedValidate;

import org.jboss.cache.Cache;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;

import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class JBCNodeStore implements DNodeStore {
	
	private static final DPath ROOT_PATH = DPaths.safeParse(ROOT_DOMAIN + ":/");
	
	@Inject
	private Cache<String, String> cache;
	
	@Inject
	private PathNormalizer paths;
	
	@PostConstruct
	public void init() {
		checkDomainRoots();
	}
	
	@PreDestroy
	public void destroy() {
		cache.stop();
	}
	
	@Override
	public List<DNode> getDomainNodes() {
		Fqn<String> fqn = toFqn(DOMAINS);
		Set<Node<String,String>> children = cache.getRoot().getChild(fqn).getChildren();
		return toJdbcNodeList(this, children);
	}
	
	@Override
	public DNode getRootDomainNode() {
		return toJbcNode(this, ROOT_PATH, cache.getNode(ROOT));
	}

	@Override
	public void destroy(DPath path) {
		notNull(path);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		cache.getRoot().removeChild(fqn);
	}

	@Override
	public DNode get(DPath path, boolean create) {
		notNull(path);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		Node<String, String> child = cache.getRoot().getChild(fqn);
		if(child == null && create) {
			child = cache.getRoot().addChild(fqn);
		}
		return toJbcNode(this, path, child);
	}
	
	@Override
	public DNode get(DPath path) {
		return get(path, false);
	}
	
	@Override
	public boolean exists(DPath path) {
		notNull(path);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		return cache.getRoot().hasChild(fqn);
	}

	@Override
	public String get(DPath path, String attribute) {
		ExtendedValidate.notNull(path, attribute);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		Node<String, String> n = cache.getRoot().getChild(fqn);
		return (n == null ? null : n.get(attribute));
	}

	@Override
	public String remove(DPath path, String attribute) {
		ExtendedValidate.notNull(path, attribute);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		Node<String, String> n = cache.getRoot().getChild(fqn);
		if(n != null) {
			return n.remove(attribute);
		} else {
			return null;
		}
	}

	@Override
	public String set(DPath path, String attribute, String value) {
		ExtendedValidate.notNull(path, attribute, value);
		Fqn<String> fqn = toFqn(paths.toJbc(path));
		Node<String, String> n = cache.getRoot().getChild(fqn);
		if(n != null) {
			return n.put(attribute, value);
		} else {
			throw new NoSuchDNodeException(path);
		}
	}
	
	
	// --- PRIVATE METHODS --- //
	
	private void checkDomainRoots() {
		checkRootDomainRoot();
		checkDomainRoot();
	}

	private void checkDomainRoot() {
		if(cache.getNode(DOMAINS) == null) {
			cache.getRoot().addChild(toFqn(PathNormalizer.DOMAINS));
		}
	}

	private Fqn<String> toFqn(String path) {
		return Fqn.fromString(path);
	}

	private void checkRootDomainRoot() {
		if(cache.getNode(ROOT) == null) {
			cache.getRoot().addChild(toFqn(PathNormalizer.ROOT));
		}
	}
}
